package com.econ.powercloud.ems.service.biz;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.common.base.facade.pojo.PageResult;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.DitTypeEnum;
import com.econ.powercloud.ems.common.enums.CacheTopicType;
import com.econ.powercloud.ems.common.enums.OperationTypeEnum;
import com.econ.powercloud.ems.common.enums.TimingControlStatusEnum;
import com.econ.powercloud.ems.common.pojo.CacheOperationVO;
import com.econ.powercloud.ems.common.pojo.EmsTimingDTO;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.EmsDevice;
import com.econ.powercloud.ems.dal.entity.EmsTimingControl;
import com.econ.powercloud.ems.dal.entity.EmsTimingControlDetail;
import com.econ.powercloud.ems.dal.entity.IotDeviceYkYtDit;
import com.econ.powercloud.ems.pojo.DitDataExtraVO;
import com.econ.powercloud.ems.pojo.DitDataVO;
import com.econ.powercloud.ems.pojo.EmsTimingControlDetailVO;
import com.econ.powercloud.ems.pojo.EmsTimingControlVO;
import com.econ.powercloud.ems.pojo.requestVO.EmsTimingControlRequestVO;
import com.econ.powercloud.ems.service.dao.EmsDeviceDAO;
import com.econ.powercloud.ems.service.dao.EmsTimingControlDAO;
import com.econ.powercloud.ems.service.dao.EmsTimingControlDetailDAO;
import com.econ.powercloud.ems.service.dao.IotDeviceYkYtDitDAO;
import com.econ.powercloud.ems.util.NetUtils;
import com.econ.powercloud.ems.util.SendKafkaDateUtil;
import com.econ.powercloud.powernode.gateway.base.utils.CurrentUserUtil;
import com.econ.powercloud.powernode.gateway.common.domain.UserDTO;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description :
 * @Date : 2023/8/15 15:16
 * @Modified by : zjf
 */
@Service
@RequiredArgsConstructor
public class TimingControlService {

    private final EmsTimingControlDAO emsTimingControlDAO;
    private final EmsTimingControlDetailDAO emsTimingControlDetailDAO;
    private final DeviceService deviceService;
    private final EmsDeviceDAO emsDeviceDAO;
    private final SendKafkaDateUtil sendKafkaDateUtil;
    private final IotDeviceYkYtDitDAO iotDeviceYkYtDitDAO;

    /**
     * 获取定时控制列表
     *
     * @param pageNo
     * @param pageSize
     * @return
     */
    public PageResult<List<EmsTimingControlVO>> getList(Integer pageNo, Integer pageSize) {
        Page<EmsTimingControl> page = emsTimingControlDAO.selectPageByCondition(null, pageNo, pageSize);
        if (CollectionUtils.isEmpty(page.getRecords())) {
            return PageResult.<List<EmsTimingControlVO>>builder().data(Collections.EMPTY_LIST).pageNo(pageNo).totalCount((int) page.getTotal()).build();

        }
        List<EmsTimingControlVO> controlVOList = JacksonUtil.convertToList(page.getRecords(), EmsTimingControlVO.class);
        controlVOList.stream().forEach(d -> d.setStatusName(TimingControlStatusEnum.getDescFromCode(d.getStatus())));
        return PageResult.<List<EmsTimingControlVO>>builder().data(controlVOList).pageNo(pageNo).totalCount((int) page.getTotal()).build();
    }

    /**
     * 获取某个定时控制的详情
     *
     * @param id: 定时控制id
     */
    public EmsTimingControlVO selectInfoById(Integer id) {
        EmsTimingControl emsTimingControl = emsTimingControlDAO.getById(id);
        Assert.notNull(emsTimingControl, "未找到对应的定时控制！");
        EmsTimingControlVO controlVO = JacksonUtil.convertToObj(emsTimingControl, EmsTimingControlVO.class);
        controlVO.setStatusName(TimingControlStatusEnum.getDescFromCode(controlVO.getStatus()));
        //获取该定时控制的配置信息
        List<EmsTimingControlDetail> controlDetailList = emsTimingControlDetailDAO.selectListByTimingControlId(id);
        if (CollectionUtils.isEmpty(controlDetailList)) {
            controlVO.setData(Collections.EMPTY_LIST);
            return controlVO;
        }
        List<EmsTimingControlDetailVO> controlDetailVOList = JacksonUtil.convertToList(controlDetailList, EmsTimingControlDetailVO.class);
        //补充其他属性数据
        this.AddExtraTimingControlDetailVOInfo(controlDetailVOList);
        controlVO.setData(controlDetailVOList);
        return controlVO;
    }

    private void AddExtraTimingControlDetailVOInfo(List<EmsTimingControlDetailVO> controlDetailVOList) {
        if (CollectionUtils.isEmpty(controlDetailVOList)) {
            return;
        }
        //ems和iot设备编号
        List<String> deviceCodes = controlDetailVOList.stream().map(EmsTimingControlDetailVO::getDeviceCode).collect(Collectors.toList());
        List<String> iotDeviceCodes = controlDetailVOList.stream().map(EmsTimingControlDetailVO::getInitDeviceCode).collect(Collectors.toList());
        //设备ykyt map key=>deviceCode.dit
        List<IotDeviceYkYtDit> iotDeviceYkYtDitList = iotDeviceYkYtDitDAO.getByDeviceCode(iotDeviceCodes);
        HashMap<String,String> iotDeviceYkYtDitMap = new HashMap<>();
        iotDeviceYkYtDitList.forEach(d->{
            String key = d.getDeviceCode()+SysConstant.SPOT+d.getDit();
            String value = d.getDitName();
            iotDeviceYkYtDitMap.put(key,value);
        });
        //获取ems设备
        List<EmsDevice> emsList = emsDeviceDAO.selectByDeviceCodes(deviceCodes);
        Map<String,String> emsMap = emsList.stream().collect(Collectors.toMap(EmsDevice::getDeviceCode,EmsDevice::getDeviceName));
        //查找点号的基本信息
        controlDetailVOList.stream().forEach(d -> {
            String deviceName = emsMap.get(d.getDeviceCode());
            String ditName = iotDeviceYkYtDitMap.get(d.getInitDeviceCode()+SysConstant.SPOT+d.getDit());
            d.setDeviceName(deviceName);
            d.setDitTypeName(DitTypeEnum.getDescFromCode(d.getDitType()));
            d.setDitName(ditName);
        });
    }

    /**
     * 删除某个定时控制
     *
     * @param id: 定时控制id
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteTimingControl(Integer id) {
        EmsTimingControl emsTimingControl = emsTimingControlDAO.getById(id);
        Assert.notNull(emsTimingControl, "未找到对应的定时控制！");
        emsTimingControlDAO.getBaseMapper().deleteById(id);
        emsTimingControlDetailDAO.deleteByTimingControlId(id);
        if (Objects.equals(TimingControlStatusEnum.EFFECTIVE, emsTimingControl.getStatus())) {
            //发送kafka取消定时任务的消息，停止双机内存中的定时任务
            EmsTimingDTO emsTimingDTO = new EmsTimingDTO();
            emsTimingDTO.setId(emsTimingControl.getId());
            emsTimingDTO.setStatus(TimingControlStatusEnum.EXPIRED.getCode());
            List<CacheOperationVO> sendData = sendKafkaDateUtil.getSendData(OperationTypeEnum.DELETE, Lists.newArrayList(emsTimingDTO), CacheTopicType.TIMING_SYNC);
            sendKafkaDateUtil.sendCommonOperationData(sendData);
        }
    }

    /**
     * 修改某个定时控制的状态
     *
     * @param id: 定时控制id
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateStatusById(Integer id, Integer status) {
        EmsTimingControl emsTimingControl = emsTimingControlDAO.getById(id);
        Assert.notNull(emsTimingControl, "未找到对应的定时控制！");
        if (Objects.equals(status, emsTimingControl.getStatus())) {
            throw new CommonRuntimeException(StatusType.FAIL, "该定时任务状态与修改的状态一致,无需修改!");
        }
        UserDTO userDTO = CurrentUserUtil.get();
        emsTimingControl.setStatus(status);
        emsTimingControl.setModifyBy(userDTO != null ? userDTO.getId() : null);
        emsTimingControl.setGmtModify(LocalDateTime.now());
        emsTimingControl.setClientIp(NetUtils.getClientIp());
        emsTimingControlDAO.updateById(emsTimingControl);
        //启停任务，同步通知kafka
        EmsTimingDTO emsTimingDTO = new EmsTimingDTO();
        emsTimingDTO.setId(emsTimingControl.getId());
        emsTimingDTO.setStatus(status);
        OperationTypeEnum opType = status.equals(OperationTypeEnum.DELETE.getCode()) ? OperationTypeEnum.DELETE : OperationTypeEnum.ADD;
        List<CacheOperationVO> sendData = sendKafkaDateUtil.getSendData(opType, Lists.newArrayList(emsTimingDTO), CacheTopicType.TIMING_SYNC);
        sendKafkaDateUtil.sendCommonOperationData(sendData);
    }

    /**
     * 新增单个定时控制(新增的定时控制默认都是未生效)
     *
     * @param requestVO
     */
    @Transactional(rollbackFor = Exception.class)
    public void insertTimingControl(EmsTimingControlRequestVO requestVO) {
        //判断名称是否重复
        List<String> nameList = emsTimingControlDAO.list().stream().map(d -> d.getName()).collect(Collectors.toList());
        if (nameList.contains(requestVO.getName())) {
            throw new CommonRuntimeException(StatusType.FAIL, "定时控制名称不允许重复！");
        }
        UserDTO userDTO = CurrentUserUtil.get();
        //获取定时任务的定时时间cron
        String cron = this.getCron(requestVO.getTime());
        EmsTimingControl emsTimingControl = EmsTimingControl.builder()
                .name(requestVO.getName())
                .time(requestVO.getTime())
                .cron(cron)
                .status(TimingControlStatusEnum.EXPIRED.getCode())
                .clientIp(NetUtils.getClientIp())
                //此处暂时只有一个任务类型,先默认使用该类型任务去执行
                .beanName("scheduleGroupControlTask")
                .methodName("execute")
                .gmtCreate(LocalDateTime.now())
                .gmtModify(LocalDateTime.now())
                .createBy(userDTO != null ? userDTO.getId() : null)
                .build();
        emsTimingControlDAO.getBaseMapper().insert(emsTimingControl);
        List<EmsDevice> deviceList = emsDeviceDAO.selectByDeviceCodes(requestVO.getData().stream().map(d -> d.getDeviceCode()).collect(Collectors.toList()));
        //key:deviceCode,value:initDeviceCode
        Map<String, String> deviceMap = deviceList.stream().collect(Collectors.toMap(EmsDevice::getDeviceCode, EmsDevice::getInitDeviceCode));
        List<EmsTimingControlDetail> controlDetailList = requestVO.getData().stream()
                .map(d -> EmsTimingControlDetail.builder()
                        .timingControlId(emsTimingControl.getId())
                        .initDeviceCode(deviceMap.get(d.getDeviceCode()))
                        .deviceCode(d.getDeviceCode())
                        .ditType(d.getDitType())
                        .dit(d.getDit())
                        .val(d.getVal())
                        .gmtCreate(LocalDateTime.now())
                        .gmtModify(LocalDateTime.now())
                        .build())
                .collect(Collectors.toList());
        emsTimingControlDetailDAO.saveBatch(controlDetailList);
    }

    /**
     * 修改单个定时控制
     *
     * @param id: 定时控制id
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateTimingControl(Integer id, EmsTimingControlRequestVO requestVO) {
        EmsTimingControl emsTimingControl = emsTimingControlDAO.getById(id);
        Assert.notNull(emsTimingControl, "未找到对应的定时控制！");
        List<String> nameList = emsTimingControlDAO.list().stream().filter(d -> !Objects.equals(id, d.getId()))
                .map(d -> d.getName()).collect(Collectors.toList());
        if (nameList.contains(requestVO.getName())) {
            throw new CommonRuntimeException(StatusType.FAIL, "定时控制名称不允许重复！");
        }
        UserDTO userDTO = CurrentUserUtil.get();
        emsTimingControl.setName(requestVO.getName());
        emsTimingControl.setTime(requestVO.getTime());
        emsTimingControl.setCron(this.getCron(requestVO.getTime()));
        emsTimingControl.setClientIp(NetUtils.getClientIp());
        emsTimingControl.setModifyBy(userDTO != null ? userDTO.getId() : null);
        emsTimingControl.setGmtModify(LocalDateTime.now());
        emsTimingControlDAO.updateById(emsTimingControl);
        List<EmsDevice> deviceList = emsDeviceDAO.selectByDeviceCodes(requestVO.getData().stream().map(d -> d.getDeviceCode()).collect(Collectors.toList()));
        //key:deviceCode,value:initDeviceCode
        Map<String, String> deviceMap = deviceList.stream().collect(Collectors.toMap(EmsDevice::getDeviceCode, EmsDevice::getInitDeviceCode));
        List<EmsTimingControlDetail> controlDetailList = requestVO.getData().stream()
                .map(d -> EmsTimingControlDetail.builder()
                        .timingControlId(id)
                        .initDeviceCode(deviceMap.get(d.getDeviceCode()))
                        .deviceCode(d.getDeviceCode())
                        .ditType(d.getDitType())
                        .dit(d.getDit())
                        .val(d.getVal())
                        .gmtCreate(LocalDateTime.now())
                        .gmtModify(LocalDateTime.now())
                        .build())
                .collect(Collectors.toList());
        emsTimingControlDetailDAO.deleteByTimingControlId(id);
        emsTimingControlDetailDAO.saveBatch(controlDetailList);
        if (Objects.equals(TimingControlStatusEnum.EFFECTIVE.getCode(), emsTimingControl.getStatus())) {
            //如果修改时任务生效中,则重启定时任务
            EmsTimingDTO emsTimingDTO = new EmsTimingDTO();
            emsTimingDTO.setId(emsTimingControl.getId());
            emsTimingDTO.setStatus(TimingControlStatusEnum.EFFECTIVE.getCode());
            List<CacheOperationVO> sendData = sendKafkaDateUtil.getSendData(OperationTypeEnum.REFRESH, Lists.newArrayList(emsTimingDTO), CacheTopicType.TIMING_SYNC);
            sendKafkaDateUtil.sendCommonOperationData(sendData);
        }
    }

    /**
     * 补充定时控制配置的其他属性
     *
     * @param controlDetailVOList
     */
    private void initTimingControlDetailVOInfo(List<EmsTimingControlDetailVO> controlDetailVOList) {
        if (CollectionUtils.isEmpty(controlDetailVOList)) {
            return;
        }
        //查询设备点号的基本信息
        List<DitDataVO> ditList = controlDetailVOList.stream().map(d -> DitDataVO.builder()
                .initDeviceCode(d.getInitDeviceCode()).ditType(d.getDitType()).dit(d.getDit()).build()).collect(Collectors.toList());
        //查找点号的基本信息
        List<DitDataExtraVO> extraVOList = deviceService.getDitExtraByDitDataList(ditList);
        Map<String, DitDataExtraVO> extraMap = extraVOList.stream().collect(Collectors.toMap(d -> d.getInitDeviceCode() + SysConstant.SPOT + d.getDit(), p -> p, (p1, p2) -> p1));
        controlDetailVOList.stream().forEach(d -> {
            DitDataExtraVO extraVO = extraMap.get(d.getInitDeviceCode() + SysConstant.SPOT + d.getDit());
            d.setDeviceName(extraVO == null ? null : extraVO.getDeviceName());
            d.setDitTypeName(DitTypeEnum.getDescFromCode(d.getDitType()));
            d.setDitName(extraVO == null ? null : extraVO.getDitName());
        });
    }

    /**
     * HH:mm:ss格式的字符串转化为cron表达式
     *
     * @param timeStr
     * @return
     */
    private String getCron(String timeStr) {
        // "HH:mm:ss" 根据时间字符串 定义的 转换 格式
        LocalTime time = LocalTime.parse(timeStr, DateTimeFormatter.ofPattern("HH:mm:ss"));
        // "ss mm HH * * ?" 要转换的 cron 表达式的格式
        String cron = time.format(DateTimeFormatter.ofPattern("ss mm HH * * ?"));
        return cron;
    }

}
